package com.blyat.xsoft.gateway.security.core;

import com.blyat.xsoft.gateway.ribbon.router.AuthenticateRouteService;
import com.google.common.collect.Sets;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Set;

/**
 * @Auther: syh
 * @Date: 2020/8/27
 * @Description:
 */
public class XSoftReactiveAuthorizationManager
        implements ReactiveAuthorizationManager<AuthorizationContext>, SmartInitializingSingleton {
    private static final Logger logger = LoggerFactory.getLogger(XSoftReactiveAuthorizationManager.class);
    private Set<String> authenticatePath = Sets.newConcurrentHashSet();
    // 全部拦截/部分拦截/全部放行/部分放行
    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private AuthenticateRouteService authenticateRouteService;

    public AuthenticateRouteService getAuthenticateRouteService() {
        return authenticateRouteService;
    }

    public void setAuthenticateRouteService(AuthenticateRouteService authenticateRouteService) {
        this.authenticateRouteService = authenticateRouteService;
    }

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        ServerWebExchange exchange = authorizationContext.getExchange();
        // 请求资源
        String requestPath = exchange.getRequest().getURI().getPath();
        // 是否认证
        if (authenticatePath(requestPath)) {
            return authentication
                    .map(auth -> new AuthorizationDecision(checkAuthorities(exchange, auth, requestPath)))
                    .defaultIfEmpty(new AuthorizationDecision(false));
        }

        // 放行
        return Mono.just(new AuthorizationDecision(true));
    }

    /**
     * 校验是否放行
     *
     * @param requestPath 请求路径
     * @return
     */
    private boolean authenticatePath(String requestPath) {
        return authenticatePath.stream()
                .filter(r -> antPathMatcher.match(r, requestPath)).findFirst().isPresent();
    }

    //权限校验
    private boolean checkAuthorities(ServerWebExchange exchange, Authentication auth, String requestPath) {
        if (auth instanceof OAuth2Authentication) {
            OAuth2Authentication athentication = (OAuth2Authentication) auth;
            String clientId = athentication.getOAuth2Request().getClientId();
            // logger.info("client:{}", clientId);
        }

        Object principal = auth.getPrincipal();
        // logger.info("username:{}", principal.toString());
        return true;
    }

    /**
     * 需要身份验证的路径
     */
    @Override
    public void afterSingletonsInstantiated() {
        authenticatePath.add("/gateway/current/**");
        if (authenticateRouteService != null) {
            authenticatePath.addAll(authenticateRouteService.getAuthenticatePath());
        }
    }
}
